package com.base.pub.persistence;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.orm.hibernate5.SessionFactoryUtils;
import org.springframework.orm.hibernate5.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.base.pub.util.ThreadLoclCache;

public abstract class BaseDao implements IBaseDao {

	private final static Logger LOGGER = LoggerFactory.getLogger(BaseDao.class);

	@Autowired
	private LocalSessionFactoryBean sqlSessionFactory;

	private DaoConfig config;

	/**
	 * @description:
	 * @author songxy DateTime 2017年5月25日 上午11:47:01
	 * @return
	 */

	@Override
	public SessionFactory getSessionFactory() {
		return this.sqlSessionFactory.getObject();
	}

	protected <T> List<T> getPageList(int startIdx, int pageSize)
			throws DataAccessException {
		return null;
	}

	/**
	 * @description:
	 * @author songxy DateTime 2017年5月25日 上午11:47:01
	 * @param entityClass
	 * @param ids
	 * @return
	 */

	@Override
	public <T> List<T> getEntityByIds(Class<T> entityClass, Long[] ids) {
		if (ids != null && ids.length > 0) {
			List<T> data = new ArrayList<>();
			for (Long l : ids) {
				T r = get(entityClass, l);
				data.add(r);
			}
			return data;
		}
		return null;
	}

	protected void remove(Long id) {

	}

	/**
	 * @description:
	 * @author songxy DateTime 2017年5月25日 上午11:47:01
	 * @param entityName
	 * @return
	 * @throws DataAccessException
	 */

	@Override
	public Long getCount(String entityName) throws DataAccessException {
		Session session = null;
		String queryString = "select count(*) from ".concat(entityName);
		try {
			Query queryObject = session.createQuery(queryString);
			prepareQuery(queryObject);
			return queryObject.getFirstResult().longValue();
		} catch (HibernateException ex) {
			throw SessionFactoryUtils.convertHibernateAccessException(ex);
		} finally {
			if (session != null) {
				SessionFactoryUtils.closeSession(session);
			}
		}
	}

	/**
	 * @description:
	 * @author songxy DateTime 2017年5月25日 上午11:47:01
	 * @param entity
	 * @throws DataAccessException
	 */

	@Override
	public void update(Object entity) throws DataAccessException {
		Session session = null;
		try {
			session = getSession();
			checkWrite(session);
			session.update(entity);
		} catch (HibernateException ex) {
			throw SessionFactoryUtils.convertHibernateAccessException(ex);
		} finally {
			if (session != null) {
				SessionFactoryUtils.closeSession(session);
			}
		}
	}

	/**
	 * @description:
	 * @author songxy DateTime 2017年5月25日 上午11:47:01
	 * @param entity
	 * @throws DataAccessException
	 */

	@Override
	public <T> void saveOrUpdate(T entity) throws DataAccessException {

		Session session = null;
		try {
			session = getSession();
			checkWrite(session);
			session.saveOrUpdate(entity);
		} catch (HibernateException ex) {
			throw SessionFactoryUtils.convertHibernateAccessException(ex);
		} finally {
			if (session != null) {
				SessionFactoryUtils.closeSession(session);
			}
		}
	}

	/**
	 * @description:
	 * @author songxy DateTime 2017年5月25日 上午11:47:01
	 * @param entityClass
	 * @param id
	 * @return
	 * @throws DataAccessException
	 */

	@Override
	public <T> T get(Class<T> entityClass, Serializable id)
			throws DataAccessException {
		Session session = null;
		try {
			session = getSession();
			return (T) session.get(entityClass, id);
		} catch (HibernateException ex) {
			throw SessionFactoryUtils.convertHibernateAccessException(ex);
		} finally {
			if (session != null) {
				SessionFactoryUtils.closeSession(session);
			}
		}
	}

	/**
	 * @description:
	 * @author songxy DateTime 2017年5月25日 上午11:47:01
	 * @param entityClass
	 * @return
	 * @throws DataAccessException
	 */

	@Override
	public <T> List<T> findAll(final Class<T> entityClass)
			throws DataAccessException {
		Session session = null;
		try {
			session = getSession();
			Criteria criteria = session.createCriteria(entityClass);
			criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
			prepareCriteria(criteria);
			return criteria.list();
		} catch (HibernateException ex) {
			throw SessionFactoryUtils.convertHibernateAccessException(ex);
		} finally {
			if (session != null) {
				SessionFactoryUtils.closeSession(session);
			}
		}
	}

	/**
	 * @description:
	 * @author songxy DateTime 2017年5月25日 上午11:47:01
	 * @param queryString
	 * @param args
	 * @return
	 * @throws DataAccessException
	 */

	@Override
	public List<?> findAll(String queryString, Object... args)
			throws DataAccessException {
		Session session = null;
		try {
			session = getSession();
			Query queryObject = session.createQuery(queryString);
			prepareQuery(queryObject);
			if (args != null && args.length > 0) {
				for (int i = 0; i < args.length; i++) {
					queryObject.setParameter(i, args[i]);
				}
			}
			return queryObject.list();
		} catch (HibernateException ex) {
			throw SessionFactoryUtils.convertHibernateAccessException(ex);
		} finally {
			if (session != null) {
				SessionFactoryUtils.closeSession(session);
			}
		}
	}

	private void checkWrite(Session session) {
		if (session.getFlushMode().lessThan(FlushMode.COMMIT)) {
			session.setFlushMode(FlushMode.COMMIT);
		}
	}

	private void prepareCriteria(Criteria criteria) {
		if (config.isCacheQueries()) {
			criteria.setCacheable(true);
			if (config.getQueryCacheRegion() != null) {
				criteria.setCacheRegion(config.getQueryCacheRegion());
			}
		}
		if (config.getFetchSize() > 0) {
			criteria.setFetchSize(config.getFetchSize());
		}
		if (config.getMaxResults() > 0) {
			criteria.setMaxResults(config.getMaxResults());
		}

		SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
				.getResource(getSession().getSessionFactory());
		if (sessionHolder != null && sessionHolder.hasTimeout()) {
			criteria.setTimeout(sessionHolder.getTimeToLiveInSeconds());
		}
	}

	private void prepareQuery(Query queryObject) {
		if (config.isCacheQueries()) {
			queryObject.setCacheable(true);
			if (config.getQueryCacheRegion() != null) {
				queryObject.setCacheRegion(config.getQueryCacheRegion());
			}
		}
		if (config.getFetchSize() > 0) {
			queryObject.setFetchSize(config.getFetchSize());
		}
		if (config.getMaxResults() > 0) {
			queryObject.setMaxResults(config.getMaxResults());
		}

		SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
				.getResource(getSessionFactory());
		if (sessionHolder != null && sessionHolder.hasTimeout()) {
			queryObject.setTimeout(sessionHolder.getTimeToLiveInSeconds());
		}
	}

	private Session getSession() {
		Session session = null;
		try {
			session = getSessionFactory().getCurrentSession();
		} catch (HibernateException ex) {
			LOGGER.debug("Could not find current session", ex);
		}

		if (session == null) {
			session = getSessionFactory().openSession();
			session.setFlushMode(FlushMode.MANUAL);
		}

		String tenantId = (String) ThreadLoclCache.get();

		if (tenantId != null) {
			session.getSessionFactory().withOptions()
					.tenantIdentifier(tenantId);
		}
		return session;
	}
}
